﻿using System;
using System.Collections.Generic;

namespace Yaml.Presentation.Parsing {
    public class SimpleKeys {
        /// <summary>
        /// Can a simple key start at the current position?
        /// </summary>
        private bool allowSimpleKey;
        private Dictionary<int, SimpleKey> possibleSimpleKeys;

        public SimpleKeys() {
            allowSimpleKey = false;
            this.possibleSimpleKeys = new Dictionary<int, SimpleKey>();
        }

        public Dictionary<int, SimpleKey> PossibleSimpleKeys {
            get { return this.possibleSimpleKeys; }
        }

        /// <summary>
        /// Are we allowed to have a simple key at the current scan position?
        /// </summary>
        public bool IsSimpleKeyAllowed {
            get { return allowSimpleKey; }
            set { allowSimpleKey = value; }
        }

        /// <summary>
        /// The next token might be a simple key, save its number and position
        /// </summary>
        public void SaveSimpleKey(int flowDepth, SimpleKey key) {
            System.Diagnostics.Debug.Assert(IsSimpleKeyAllowed || !key.Required);

            if (IsSimpleKeyAllowed) {
                RemoveSimpleKey(flowDepth);
                possibleSimpleKeys[flowDepth] = key;
            }
        }

        /// <summary>
        /// Remove simple keys that can not be valid given the current position.
        /// YAML specifies that simple keys should be :
        /// - no longer than 1024 characters
        /// - limited to a single line
        /// </summary>
        /// <param name="currentPosition"></param>
        public void RemoveInvalidSimpleKeys(Mark currentPosition) {
            System.Collections.Generic.List<int> levels = new List<int>(possibleSimpleKeys.Keys);
            foreach (int level in levels) {
                SimpleKey simpleKey = possibleSimpleKeys[level];
                if (simpleKey.Line != currentPosition.Line || (simpleKey.Index - currentPosition.CharacterIndex) > 1024) {
                    if (simpleKey.Required) {
                        throw CreateSimpleKeyException(simpleKey.Mark);
                    }
                    possibleSimpleKeys.Remove(level);
                }
            }
        }
        /// <summary>
        /// Get the simple key that is associated with the earliest token.
        /// </summary>
        public SimpleKey GetNearestSimpleKey() {
            SimpleKey minKey = null;
            foreach (SimpleKey key in possibleSimpleKeys.Values) {
                if (minKey == null || key.TokenNumber < minKey.TokenNumber) {
                    minKey = key;
                }
            }
            return minKey;
        }

        /// <summary>
        /// Remove the simple key at the specified flow level, checking that the key was not required.
        /// </summary>
        public SimpleKey RemoveSimpleKey(int flowDepth) {
            SimpleKey key = null;
            if ( possibleSimpleKeys.TryGetValue(flowDepth, out key) ) {
                if ( key.Required ) {
                    throw CreateSimpleKeyException(key.Mark); 
                }
                possibleSimpleKeys.Remove(flowDepth);
            }
            return key;
        }

        private ScannerException CreateSimpleKeyException(Mark keyPosition) {
            return new ScannerException("while scanning a simple key " + keyPosition.ToString() + " could not find expected ':' ");
        }


        public void Reset() {
            allowSimpleKey = false;
            possibleSimpleKeys.Clear();
        }

        /// <summary>
        /// Do we have a simple key ready for this point in the scan?
        /// </summary>
        public bool IsKeyValid(int flowDepth) {
            return possibleSimpleKeys.ContainsKey(flowDepth);
        }
    }
}
